package com.audaque.springboot.foshanupload.core.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.PatternPool;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author zgb
 * @desc ...
 * @date 2022-06-06 16:19:42
 */

/**
 * 身份证相关工具类<br>
 * see https://www.oschina.net/code/snippet_1611_2881
 *
 * <p>
 * 本工具并没有对行政区划代码做校验，如有需求，请参阅（2018年10月）：
 * http://www.mca.gov.cn/article/sj/xzqh/2018/201804-12/20181011221630.html
 * </p>
 *
 * @author Looly
 * @since 3.0.4
 */


public class IdcardUtilExtHutool extends IdcardUtil {

    /**
     * 中国公民身份证号码最小长度。
     */
    public static final int CHINA_ID_MIN_LENGTH = 15;
    /**
     * 中国公民身份证号码最大长度。
     */
    public static final int CHINA_ID_MAX_LENGTH = 18;
    /**
     * 每位加权因子
     */
    public static final int[] POWER = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    /**
     * 省市代码表
     */
    public static final Map<String, String> CITY_CODES = new HashMap<>();
    /**
     * 台湾身份首字母对应数字
     */
    public static final Map<String, Integer> TW_FIRST_CODE = new HashMap<>();

    static {
        CITY_CODES.put("11", "北京");
        CITY_CODES.put("12", "天津");
        CITY_CODES.put("13", "河北");
        CITY_CODES.put("14", "山西");
        CITY_CODES.put("15", "内蒙古");
        CITY_CODES.put("21", "辽宁");
        CITY_CODES.put("22", "吉林");
        CITY_CODES.put("23", "黑龙江");
        CITY_CODES.put("31", "上海");
        CITY_CODES.put("32", "江苏");
        CITY_CODES.put("33", "浙江");
        CITY_CODES.put("34", "安徽");
        CITY_CODES.put("35", "福建");
        CITY_CODES.put("36", "江西");
        CITY_CODES.put("37", "山东");
        CITY_CODES.put("41", "河南");
        CITY_CODES.put("42", "湖北");
        CITY_CODES.put("43", "湖南");
        CITY_CODES.put("44", "广东");
        CITY_CODES.put("45", "广西");
        CITY_CODES.put("46", "海南");
        CITY_CODES.put("50", "重庆");
        CITY_CODES.put("51", "四川");
        CITY_CODES.put("52", "贵州");
        CITY_CODES.put("53", "云南");
        CITY_CODES.put("54", "西藏");
        CITY_CODES.put("61", "陕西");
        CITY_CODES.put("62", "甘肃");
        CITY_CODES.put("63", "青海");
        CITY_CODES.put("64", "宁夏");
        CITY_CODES.put("65", "新疆");
        CITY_CODES.put("71", "台湾");
        CITY_CODES.put("81", "香港");
        CITY_CODES.put("82", "澳门");
        //issue#1277，台湾身份证号码以83开头，但是行政区划为71
        CITY_CODES.put("83", "台湾");
        CITY_CODES.put("91", "国外");

        TW_FIRST_CODE.put("A", 10);
        TW_FIRST_CODE.put("B", 11);
        TW_FIRST_CODE.put("C", 12);
        TW_FIRST_CODE.put("D", 13);
        TW_FIRST_CODE.put("E", 14);
        TW_FIRST_CODE.put("F", 15);
        TW_FIRST_CODE.put("G", 16);
        TW_FIRST_CODE.put("H", 17);
        TW_FIRST_CODE.put("J", 18);
        TW_FIRST_CODE.put("K", 19);
        TW_FIRST_CODE.put("L", 20);
        TW_FIRST_CODE.put("M", 21);
        TW_FIRST_CODE.put("N", 22);
        TW_FIRST_CODE.put("P", 23);
        TW_FIRST_CODE.put("Q", 24);
        TW_FIRST_CODE.put("R", 25);
        TW_FIRST_CODE.put("S", 26);
        TW_FIRST_CODE.put("T", 27);
        TW_FIRST_CODE.put("U", 28);
        TW_FIRST_CODE.put("V", 29);
        TW_FIRST_CODE.put("X", 30);
        TW_FIRST_CODE.put("Y", 31);
        TW_FIRST_CODE.put("W", 32);
        TW_FIRST_CODE.put("Z", 33);
        TW_FIRST_CODE.put("I", 34);
        TW_FIRST_CODE.put("O", 35);
    }

    public static String checkGat(Object obj) {
        if (obj == null) {
            return "0";
        }
        String vcid = obj.toString();
        if (StringUtils.isBlank(vcid)) {
            return "0";
        }
        String s = check10(obj);
        if (Objects.equals(s, "0")) {
            if (ReUtil.isMatch("^[a-zA-Z][0-9]{9}$", vcid)) {
                return "1";
            }
            if (ReUtil.isMatch("^((\\s?[A-Za-z])|([A-Za-z]{2}))\\d{6}(\\([0−9aA]\\)|[0-9aA])$", vcid)) {
                return "2";
            }
            if (ReUtil.isMatch("^[1|5|7][0-9]{6}[0-9A-Z]$", vcid)) {
                return "3";
            }
            return "0";
        }
        return s;
    }

    public static String check10(Object obj) {
        if (obj == null) {
            return "0";
        }
        String vcid = obj.toString();
        if (StringUtils.isBlank(vcid)) {
            return "0";
        }

        if (vcid.length() != 10) {
            return "0";
        }

        String[] info = isValidCard10(vcid);
        if (null == info || "false".equals(info[2])) {
            return "0";
        }
        if (Objects.equals(info[0], "台湾")) {
            return "1";
        }
        if (Objects.equals(info[0], "香港")) {
            return "2";
        }
        if (Objects.equals(info[0], "澳门")) {
            return "3";
        }

        return "0";
    }


    public static int check15(Object obj) {
        if (obj == null) {
            return -1;
        }
        String vcid = obj.toString();
        if (StringUtils.isBlank(vcid)) {
            return -1;
        }
        if (vcid.length() != 15) {
            return -1;
        }

        if (!ReUtil.isMatch("^[1-9][0-9]{14}$", vcid)) {
            return -1;
        }


        if (!IdcardUtil.isValidCard(vcid)) {
            return -1;
        }
        return 0;
    }


    public static int check17(Object obj) {
        if (obj == null) {
            return -1;
        }
        String vcid = obj.toString();
        if (StringUtils.isBlank(vcid)) {
            return -1;
        }
        if (vcid.length() != 17) {
            return -1;
        }
        if (!ReUtil.isMatch("^[1-9][0-9]{16}$", vcid)) {
            return -1;
        }


        if (!ReUtil.isMatch(PatternPool.NUMBERS, vcid)) {
            return -1;
        }


        // 省份
        String proCode = vcid.substring(0, 2);
        if (null == CITY_CODES.get(proCode)) {
            return -1;
        }

        //校验生日
        if (!Validator.isBirthday(vcid.substring(6, 14))) {
            return -1;
        }

        return 0;
    }


    public static String check18(Object obj) {
        if (obj == null) {
            return null;
        }
        String vcid = obj.toString();
        if (StringUtils.isBlank(vcid)) {
            return null;
        }
        if (vcid.length() != 18) {
            return null;
        }
        if (!ReUtil.isMatch("^[0-9]{17}[0-9X]$", vcid)) {
            return null;
        }


        if (!IdcardUtil.isValidCard(vcid)) {
            return null;
        }


        return vcid;
    }


    public static String convert15To18(Object obj) {
        int check = check15(obj);
        if (check == -1) {
            return null;
        }
        String vcid = obj.toString();
        return convert15To18(vcid);
    }


    public static String convert17To18(Object obj) {
        int check = check17(obj);
        if (check == -1) {
            return null;
        }
        String idCard = obj.toString();

        StringBuilder idCard18;
        if (idCard.length() != CHINA_ID_MIN_LENGTH) {
            return null;
        }
        if (ReUtil.isMatch(PatternPool.NUMBERS, idCard)) {
            // 获取出生年月日
            String birthday = idCard.substring(6, 14);
            Date birthDate = DateUtil.parse(birthday, "yyMMdd");
            // 获取出生年(完全表现形式,如：2010)
            int sYear = DateUtil.year(birthDate);
            if (sYear > 2000) {
                // 2000年之后不存在15位身份证号，此处用于修复此问题的判断
                sYear -= 100;
            }
            idCard18 = StrUtil.builder().append(idCard, 0, 6).append(sYear).append(idCard.substring(8));
            // 获取校验位
            char sVal = getCheckCode18(idCard18.toString());
            idCard18.append(sVal);
        } else {
            return null;
        }
        return idCard18.toString();

    }


    /**
     * 获得18位身份证校验码
     *
     * @param code17 18位身份证号中的前17位
     * @return 第18位
     */
    public static char getCheckCode18(String code17) {
        return getCheckCode18(code17);
    }


    public static String to18(Object obj) {
        if (check15(obj) == 0) {
            return convert15To18(obj);
        }


        if (check17(obj) == 0) {
            return convert17To18(obj);
        }

        return check18(obj);

    }

    public static String getGenderFromId(Object obj) {
        if (obj == null) {
            return null;
        }
        String input = obj.toString();

        String idcard = to18(obj);
        char sCardChar = Objects.requireNonNull(idcard).charAt(16);
        int i = sCardChar % 2;
        String result = null;
        switch (i) {
            case 0:
                result = "2";
                break;
            case 1:
                result = "1";
                break;
            default:
                result = null;
                break;
        }
        return result;
    }


    public static String getAgeFromId(Object obj) {
        if (obj == null) {
            return null;
        }
        String input = obj.toString();

        String idcard = to18(obj);
        int age = IdcardUtil.getAgeByIdCard(idcard);
        return String.valueOf(age);

    }


}
